// Copyright 2025 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <chrono>
#include <mutex>
#include "CaeConnectionAgent.h"
#include "../cae_agent/CaeAgent.h"
#include "CaeMediaConfig.h"
#include "CaeParamStorage.h"
#include "../cae_common/CaeMsgCode.h"
#include "../cae_common/CaeAppCtrlCmdUtils.h"
#include "net_trans_def.h"

#include <utility>

namespace MTRANS {
    enum BitrateRange : int {
        P1080_60FPS = 1,
        P720_60FPS,
        P720_40FPS,
        P720_30FPS,
        P540_30FPS,
        P480_10FPS,
        BITRATE_RANGE_MAX
    };

    static uint32_t gLastBitrate = 0;
    static BitrateRange gLastRange = P1080_60FPS;
    static int gDownRangeCount = 2;
}

bool OnIsVerifiedIpCallBack(std::string ip);
int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvCmdData(uint8_t* data, uint32_t length);
int32_t OnRecvSensorDataStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvLocationStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvTouchEventStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvKeyEventStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvMotionEventStreamData(uint8_t* data, uint32_t length);
void OnEncBitrateCallback(uint32_t encBitrate, uint32_t totalBitrate);
void OnEncKeyFrameCallback();
bool IsBackHome();
void SendMsgToClient(int msg);
void OnRemoteAddressChanged(string ip, uint16_t port);

CaeConnectionAgent::CaeConnectionAgent()
{
    m_connectionPool = std::make_shared<CaeConnectionPool>();
}

CaeConnectionAgent::~CaeConnectionAgent()
{
    m_connectionPool->Clear();
    m_connectionPool = nullptr;
    if (m_mtrans != nullptr) {
        m_mtrans->Stop();
        m_mtrans = nullptr;
    }

    m_audioPipeDataHandleThread = nullptr;
    m_videoPipeDataHandleThread = nullptr;
    m_virtualDeviceDataHandleThread = nullptr;
}

bool CaeConnectionAgent::IsExclusiveMode()
{
    return CaeConfigManage::IsExclusiveMode() > 0;
}

void CaeConnectionAgent::AddConnector(CaeSocket *socket)
{
    LOGI("Add connector: %s", socket->GetConnectionId().c_str());

    if (socket->GetRemoteIpStr().empty()) {
        return;
    }

    std::shared_ptr<CaeSocketAgent> socketAgent = m_connectionPool->GetIfExist(socket->GetConnectionId());
    if (socketAgent == nullptr) {
        socketAgent = std::make_shared<CaeSocketAgent>();
        socketAgent->SetSocketConnectionId(socket->GetConnectionId());
        socketAgent->SetClientIp(socket->GetRemoteIpStr());
        socketAgent->SetClientPort(socket->GetRemotePortStr());
        socketAgent->SetClientSocket(socket);
        m_connectionPool->Add(socket->GetConnectionId(), socketAgent);

        socketAgent->SetVerifyCallback([this](
                bool success, bool secondVerify, const std::string& connectionId, const std::string& ClientPlatformType)
                                       {CaeConnectionAgent::VerifyResultCallback(success, secondVerify, connectionId, ClientPlatformType);});
        socketAgent->SetActionCallback(
                [this](std::string &connectionId, COMMAND_TYPE commandType)
                { CaeConnectionAgent::ActionCallback(connectionId, commandType); });
        socketAgent->SetDisconnectCallback([this](const std::string &connectionId)
                                           { CaeConnectionAgent::DisconnectCallback(connectionId); });
        socketAgent->SetDisconnectTimeoutCallback([this](const std::string &connectionId)
                                                  { CaeConnectionAgent::DisconnectTimeoutCallback(connectionId); });
    } else {
        LOGI("Connector %s already existed, state:%s.", socket->GetConnectionId().c_str(), socketAgent->GetSocketAgentStateString().c_str());
        if (socketAgent->GetSocketAgentState() == CAE::WAIT_RECONNECT
            || socketAgent->GetSocketAgentState() == CAE::PAUSED_DISCONNECT
            || socketAgent->GetSocketAgentState() == CAE::PAUSED/*pause状态下socket重连意味着断连了*/) {
            socketAgent->CloseOldSocket();
            socketAgent->SetClientSocket(socket);
            socketAgent->StopPauseTimer();
            socketAgent->SetDisconnectCallback([this](const std::string &connectionId)
                                               { CaeConnectionAgent::DisconnectCallback(connectionId); });
            socketAgent->SetDisconnectTimeoutCallback([this](const std::string &connectionId)
                                                      { CaeConnectionAgent::DisconnectTimeoutCallback(connectionId); });
            CaeEngineControl::GetInstance().OpenMediaStream();
            CaeEngineControl::GetInstance().OpenVirtualDevices(socketAgent->GetSdkVersion());
            SendVirtualDeviceStartRequest();
            socketAgent->SetSocketAgentState(CAE::RUNNING);
        } else {
            LOGI("Do nothing.");
        }
    }
    LOGI("Add connector: %s done.", socket->GetConnectionId().c_str());
}

void CaeConnectionAgent::CloseConnector()
{
    LOGI("Close connector.");

    if (m_connectionPool->Size() == 0) {
        return;
    }

    int waitCloseClientCnt = 0;
    int runningClientCnt = 0;
    int waitCloseScreenshotClientCnt = 0;
    int runningScreenshotClientCnt = 0;
    std::lock_guard<std::mutex> lock(m_mutex);
    {
        std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
        for (const auto& connection : connections) {
            CAE::CaeSocketAgentState state = connection->GetSocketAgentState();
            string connectionId = connection->GetSocketConnectionId();
            bool isScreenshot = connection->GetConnectionParams()->IsScreenshot();
            LOGI("Client %s state: %s", connectionId.c_str(), connection->GetSocketAgentStateString().c_str());
            if (state == CAE::WAIT_CLOSE) {
                if (isScreenshot) {
                    waitCloseScreenshotClientCnt++;
                } else {
                    waitCloseClientCnt++;
                }
                connection->SetSocketAgentState(CAE::DEINIT);
                connection->Reset();
                LOGI("Clear client: %s", connectionId.c_str());
                m_connectionPool->Remove(connection);
            } else if (state == CAE::RUNNING) {
                if (isScreenshot) {
                    runningScreenshotClientCnt++;
                } else {
                    runningClientCnt++;
                }
            }
        }
    }

    if (waitCloseClientCnt == 0 && waitCloseScreenshotClientCnt == 0) {
        LOGI("No client waiting for close.");
        return;
    }

    if (runningClientCnt > 0) {
        LOGI("Have another client running.");
    } else {
        CaeEngineControl::GetInstance().CloseMediaStream();
        CaeEngineControl::GetInstance().CloseVirtualDevices();
        if (CaeConfigManage::EnableRemoteIme()) {
            CaeImeControl::GetInstance().DisableRemoteIme();
        }

        DeinitPipeDataHandlers();
        DeinitMtrans();
        CaeAgent::GetInstance().ClearPipesData();
        CaeMediaConfig::GetInstance().ClearMediaConfig();
        LOGI("Close all connectors done, CAE ready for new client.");
    }

    if (runningScreenshotClientCnt > 0) {
        LOGI("Have another client running.");
    } else {
        CaeEngineControl::GetInstance().CloseScreenshot();
        DeInitScreenshotPipeDataHandlers();
        CaeAgent::GetInstance().ClearScreenshotPipesData();
        LOGI("Close all screenshot connectors done.");
    }
}

int CaeConnectionAgent::SendCustomDataToClient(const std::shared_ptr<uint8_t>& data, int size, uint8_t type)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    for (const auto& connection : connections) {
        if (connection->GetConnectionParams()->IsScreenshot()) {
            continue;
        }
        return connection->Send(data.get(), size);
    }
    return 0;
}

void CaeConnectionAgent::InitMtrans(const string& clientVersion)
{
    if (m_mtrans == nullptr) {
        TransConfigParam param{};
        param.maxBandwidth = 20000;
        param.curBandwidth = 10000;
        param.minVideoSendBitrate = 750;
        param.maxVideoSendBitrate = 5000;
        m_mtrans = make_unique<NetTrans>();
        if (m_mtrans == nullptr) {
            LOGE("Failed to init mtrans, use tcp.");
        } else {
            bool isNewVersion = CaeCommon::CompareVersion(clientVersion, CLIENT_KEY_VERSION) > 0;
            int port = isNewVersion ? 7000 : 7002;
            m_mtrans->InitServer("", port, param,
                                 OnIsVerifiedIpCallBack,
                           OnRecvVideoStreamData,
                           OnRecvAudioStreamData,
                           OnEncBitrateCallback,
                           OnEncKeyFrameCallback,
                           OnRecvCmdData,
                           OnRecvSensorDataStreamData,
                           OnRecvLocationStreamData,
                           OnRecvTouchEventStreamData,
                           OnRecvKeyEventStreamData,
                           OnRecvMotionEventStreamData);
            m_mtrans->RegistRemoteAddressChanged(OnRemoteAddressChanged);
            LOGI("Init mtrans successfully.");
        }
    } else {
        LOGI("Mtrans already init.");
    }
}

void CaeConnectionAgent::StartMtrans()
{
    if (m_mtransStarted) {
        LOGI("Mtrans already start.");
        return;
    }

    if (m_mtrans != nullptr) {
        if (m_mtrans->Start() < 0) {
            LOGI("Mtrans start failed.");
            m_mtrans->Stop();
            m_mtransStarted = false;
        } else {
            m_mtransStarted = true;
        }
    }
}

void CaeConnectionAgent::StopMtrans()
{
    if (m_mtrans == nullptr) {
        return;
    }

    m_mtrans->Stop();
    m_mtransStarted = false;
}

void CaeConnectionAgent::DeinitMtrans()
{
    if (m_mtrans != nullptr) {
        m_mtrans->Stop();
        m_mtrans = nullptr;
        m_mtransStarted = false;
    }
}

bool CaeConnectionAgent::IsMtransValid(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    return  m_mtrans != nullptr && IsMtransEnable(socketAgent);
}

bool CaeConnectionAgent::IsMtransEnable(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    if (!CaeConfigManage::GetMtransEnable()) {
        return false;
    }

    string clientPlatformType = socketAgent->GetClientPlatformType();
    ServerType serverType = socketAgent->GetServerType();
    if ((serverType == TLS || serverType == TCP) && clientPlatformType != to_string(PLATFORM_WINDOWS)) {
        return true;
    }
    return false;
}

int CaeConnectionAgent::SendOrientationDataHook(const std::shared_ptr<uint8_t>& pkt, int size)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    for (const auto& connection : connections) {
        if (connection->GetConnectionParams()->IsScreenshot()) {
            continue;
        }
        return connection->Send(pkt.get(), size);
    }
    return 0;
}

int CaeConnectionAgent::SendVideoDataHook(void *pkt, int size)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    for (const auto& connection : connections) {
        if (connection->GetConnectionParams()->IsScreenshot()) {
            continue;
        }
        if (IsMtransValid(connection)) {
            return m_mtrans->SendVideoData(static_cast<uint8_t *>(pkt), size,
                                           CaeMediaConfig::GetInstance().GetVideoFrameType());
        } else {
            return connection->Send(pkt, size);
        }
    }
    return 0;
}

int CaeConnectionAgent::SendAudioDataHook(void *pkt, int size)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    for (const auto& connection : connections) {
        if (connection->GetConnectionParams()->IsScreenshot()) {
            continue;
        }
        if (IsMtransValid(connection)) {
            return m_mtrans->SendAudioData(static_cast<uint8_t *>(pkt), size);
        } else {
            return connection->Send(pkt, size);
        }
    }
    return 0;
}

int CaeConnectionAgent::SendVirtualDevDataHook(void *pkt, int size)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    for (const auto& connection : connections) {
        if (connection->GetConnectionParams()->IsScreenshot()) {
            continue;
        }
        return connection->Send(pkt, size);
    }
    return 0;
}

void CaeConnectionAgent::InitPipeDataHandlers()
{
    if (m_videoPipeDataHandleThread == nullptr) {
        CaeDataPipe* pipe = CaeAgent::GetInstance().GetDataPipe(CAE_MSG_VIDEO);
        m_videoPipeDataHandleThread = make_unique<CaeVideoPipeDataHandleThread>(pipe);
        if (m_videoPipeDataHandleThread == nullptr) {
            LOGE("Failed to create video pipe data handle thread.");
        } else {
            m_videoPipeDataHandleThread->SetVideoFrameSendHook([this](void *pkt, size_t size) ->
                                                                       int{return CaeConnectionAgent::SendVideoDataHook(pkt, size);});
            m_videoPipeDataHandleThread->SetOritentionSendHook([this](const std::shared_ptr<uint8_t>& pkt, size_t size) ->
                                                                       int{return CaeConnectionAgent::SendOrientationDataHook(pkt, size);});
            m_videoPipeDataHandleThread->Start();
            LOGI("Start video pipe data handle thread. ");
        }
    }
    if (m_audioPipeDataHandleThread == nullptr) {
        CaeDataPipe* pipe = CaeAgent::GetInstance().GetDataPipe(CAE_MSG_AUDIO);
        m_audioPipeDataHandleThread = make_unique<CaeAudioPipeDataHandleThread>(pipe);
        if (m_audioPipeDataHandleThread == nullptr) {
            LOGE("Failed to create audio Pipe data handle thread .");
        } else {
            m_audioPipeDataHandleThread->SetAudioFrameSendHook([this](void *pkt, size_t size) ->
                                                                       int{return CaeConnectionAgent::SendAudioDataHook(pkt, size);});
            m_audioPipeDataHandleThread->Start();
            LOGI("Start audio pipe data handle thread.");
        }
    }

    if (CaeConfigManage::EnableVirtualDevice()) {
        if (m_virtualDeviceDataHandleThread == nullptr) {
            CaeDataPipe* pipe = CaeAgent::GetInstance().GetDataPipe(CAE_MSG_VIRTUAL_DEVICE);
            m_virtualDeviceDataHandleThread = make_unique<CaeVirtualDeviceDataHandlerThread>(pipe);
            if (m_virtualDeviceDataHandleThread == nullptr) {
                LOGE("Failed to create virtual device data handle thread .");
            } else {
                m_virtualDeviceDataHandleThread->SetVirutalDeviceSendHook([this](void *pkt, size_t size) ->
                                                                                  int{return CaeConnectionAgent::SendVirtualDevDataHook(pkt, size);});
                m_virtualDeviceDataHandleThread->Start();
                LOGI("Start virtual device data handle thread.");
            }
        }
    }
}

void CaeConnectionAgent::DeinitPipeDataHandlers()
{
    if (m_videoPipeDataHandleThread != nullptr) {
        m_videoPipeDataHandleThread->Stop();
        while (m_videoPipeDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(VIDEO_HANDLER_THREAD_SLEEP_INTERVAL);
        }
        m_videoPipeDataHandleThread = nullptr;
    }
    if (m_audioPipeDataHandleThread != nullptr) {
        m_audioPipeDataHandleThread->Stop();
        while (m_audioPipeDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(AUDIO_HANDLER_THREAD_SLEEP_INTERVAL);
        }
        m_audioPipeDataHandleThread = nullptr;
    }
    if (m_virtualDeviceDataHandleThread != nullptr) {
        m_virtualDeviceDataHandleThread->Stop();
        while (m_virtualDeviceDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(VITURAL_DEVICE_HANDLER_THREAD_SLEEP_INTERVAL);
        }
        m_virtualDeviceDataHandleThread = nullptr;
    }
}

void CaeConnectionAgent::SendVirtualDeviceStartRequest()
{
    if (m_virtualDeviceDataHandleThread != nullptr) {
        if (CaeVirtualDeviceConfig::IsOpenCamera()) {
            LOGI("Camera is open, need send start request.");
            m_virtualDeviceDataHandleThread->HandleStartPreviewReq(CaeVirtualDeviceConfig::GetCameraTmpHeader(),
                                                                   CaeVirtualDeviceConfig::GetCameraTmpData(),
                                                                   CaeVirtualDeviceConfig::GetCameraTmpPipeData());
        }

        if (CaeVirtualDeviceConfig::IsOpenMicrophone()) {
            LOGI("Microphone is open, need send start request.");
            m_virtualDeviceDataHandleThread->HandleStartRecordReq(CaeVirtualDeviceConfig::GetMicrophoneTmpHeader(),
                                                                  CaeVirtualDeviceConfig::GetMicrophoneTmpData(),
                                                                  CaeVirtualDeviceConfig::GetMicrophoneTmpPipeData());
        }
    }
}

bool CaeConnectionAgent::ProcessConnectionMode(const std::string& connectionId, const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    shared_ptr<CaeParamStorage> currentParamStorage = socketAgent->GetConnectionParams();
    if (currentParamStorage->IsScreenshot()) {
        int otherScreenshotClientCnt = 0;
        std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
        for (const auto& connection : connections) {
            if (connection->GetSocketConnectionId() == connectionId) {
                continue;
            }
            if (!(connection->GetSocketAgentState() == CAE::RUNNING
                  || connection->GetSocketAgentState() == CAE::PAUSED
                  || connection->GetSocketAgentState() == CAE::VERIFYED)) {
                continue;
            }
            if (connection->GetConnectionParams()->IsScreenshot()) {
                otherScreenshotClientCnt++;
            }
        }
        if (otherScreenshotClientCnt >= 5) {
            CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RESOURCE_IN_USING, socketAgent);
            LOGI("The number of connect exceeds the limit, mark as wait close.");
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
            return false;
        }

        return true;
    }


    if (!IsExclusiveMode()) {
        std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
        for (const auto& connection : connections) {
            if (connection->GetSocketConnectionId() == connectionId) {
                continue;
            }
            if (connection->GetConnectionParams()->IsScreenshot()) {
                continue;
            }
            if (connection->GetSocketAgentState() == CAE::RUNNING
                || connection->GetSocketAgentState() == CAE::PAUSED
                || connection->GetSocketAgentState() == CAE::VERIFYED) {
                if (!CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_OTHERS_CONNECTED, connection)) {
                    // 给被抢客户端发送消息失败时，给抢占客户端发送抢占失败的消息
                    std::shared_ptr<CaeSocketAgent> newConnection = m_connectionPool->GetIfExist(connectionId);
                    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RESOURCE_IN_USING, newConnection);
                    newConnection->SetSocketAgentState(CAE::WAIT_CLOSE);
                    CaeAgent::GetInstance().CloseClient();
                    LOGW("Send msg [OTHERS_CONNECTED] to old client: %s failed, so send msg [RESOURCE_IN_USING] to new client: %s.",
                         connection->GetSocketConnectionId().c_str(), newConnection->GetSocketConnectionId().c_str());
                    return false;
                }
            }

            LOGI("Free current client：%s.", connection->GetSocketConnectionId().c_str());
            connection->SetSocketAgentState(CAE::DEINIT);
            connection->DisableSend();
            connection->Reset();
            m_connectionPool->Remove(connection);
        }
    } else {
        std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
        for (const auto& connection : connections) {
            if (connection->GetSocketConnectionId() == connectionId) {
                continue;
            }
            if (connection->GetConnectionParams()->IsScreenshot()) {
                continue;
            }
            if (connection->GetSocketAgentState() == CAE::RUNNING
                || connection->GetSocketAgentState() == CAE::PAUSED
                || connection->GetSocketAgentState() == CAE::PAUSED_DISCONNECT
                || connection->GetSocketAgentState() == CAE::WAIT_RECONNECT) {
                CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RESOURCE_IN_USING, connection);
                LOGI("Others connected, mark as wait close.");
                socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
                CaeAgent::GetInstance().CloseClient();
                return false;
            }
        }
    }

    return true;
}

bool CaeConnectionAgent::ProcessResource(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    if (socketAgent->GetConnectionParams()->IsScreenshot()) {
        if (m_screenshotPipeDataHandleThread == nullptr) {
            InitScreenshotDataHandlers();
        }
        return true;
    }

    if (m_videoPipeDataHandleThread == nullptr && m_mtrans == nullptr) {
        InitPipeDataHandlers();
    } else {
        CaeEngineControl::GetInstance().CloseMediaStream();
        CaeEngineControl::GetInstance().CloseVirtualDevices();
        CaeAgent::GetInstance().ClearPipesData();
    }

    StopMtrans();
    if (IsMtransEnable(socketAgent)) {
        InitMtrans(socketAgent->GetSdkVersion());
        StartMtrans();
    }

    return true;
}

bool CaeConnectionAgent::ProcessTimer(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    if (socketAgent->GetConnectionParams()->IsScreenshot()) {
        return true;
    }

    if (socketAgent->GetSocketAgentState() != CAE::WAIT_RECONNECT
        && socketAgent->GetSocketAgentState() != CAE::PAUSED_DISCONNECT) {
        if (!socketAgent->StartTrailTimer()) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
            return false;
        }
        if (!socketAgent->StartNotouchTimer()) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
            return false;
        }

        socketAgent->SetTrialTimeoutCallback([this](const std::string& connectionId)
                                             {CaeConnectionAgent::TrialTimeoutCallback(connectionId);});
        socketAgent->SetNoTouchTimeoutCallback([this](const std::string& connectionId)
                                               {CaeConnectionAgent::NotouchTimeoutCallback(connectionId);});
    }

    return true;
}

bool CaeConnectionAgent::ProcessMedia(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    if (socketAgent->GetConnectionParams()->IsScreenshot()) {
        if (QueryScreenshotClientNum() > 0) {
            return true;
        }
        uint32_t jpegWidth = socketAgent->GetConnectionParams()->GetJpegWidth();
        uint32_t jpegHeight = socketAgent->GetConnectionParams()->GetJpegHeight();
        Result ret = CaeEngineControl::GetInstance().OpenScreenShots(jpegWidth, jpegHeight);
        if (ret < SUCCESS && ret != ERR_SCREENSHOT_OPEN_REPEAT) {
            LOGE("Failed to open screenshot stream.result [%d],width [%d], height [%d].", ret, jpegWidth, jpegHeight);
            std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
            if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
                socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
                CaeAgent::GetInstance().CloseClient();
            }
            return false;
        }
        return true;
    }

    if (CaeMediaConfig::GetInstance().IsNeedSetPhysicalResolution()) {
        int ret = CaeEngineControl::GetInstance().SetPhysicalResolution();
        if (ret != SUCCESS) {
            LOGE("Failed to set physical resolution.");
            std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
            if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
                socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
                CaeAgent::GetInstance().CloseClient();
            }
            return false;
        }
    }

    if (!CaeEngineControl::GetInstance().IsSupportH265()) {
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_H265_NOT_SUPPORTED);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
        }
        return false;
    }

    if (IsMtransEnable(socketAgent)) {
        CaeMediaConfig::GetInstance().SetGopSize("240");
    } else {
        CaeMediaConfig::GetInstance().SetGopSize("3000");
    }

    Result ret = CaeEngineControl::GetInstance().OpenMediaStream();
    if (ret == H265_NOT_SUPPORT || ret == ERR_VIDEO_POOL_ELB_FAULT) {
        CaeEngineControl::GetInstance().CloseMediaStream();
        CaeAgent::GetInstance().SetStatus(CaeAgentStatus::CAE_AGENT_INIT);
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_H265_NOT_SUPPORTED);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
        }
        return false;
    }
    if (ret != SUCCESS) {
        LOGE("Failed to open media stream.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
        }
        return false;
    }

    CaeEngineControl::GetInstance().OpenVirtualDevices(socketAgent->GetSdkVersion());
    return true;
}

bool CaeConnectionAgent::ProcessNotifyToClient(const std::shared_ptr<CaeSocketAgent>& socketAgent)
{
    if (socketAgent->GetConnectionParams()->IsScreenshot()) {
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_START_SUCCESS, "");
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
            LOGE("Failed to send the CAE_START_SUCCESS params.");
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
            return false;
        }
        CaeCommon::PrintfCurrentTime("Open screenshot success.");
        return true;
    }

    char* rspJson = CaeMediaConfig::GetInstance().BuildResponseVideoJsonConfig();
    if (rspJson == nullptr) {
        LOGE("Failed to open media stream.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
            socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
        }
        return false;
    }

    std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_START_SUCCESS, rspJson);
    if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, socketAgent)) {
        LOGE("Failed to send the CAE_START_SUCCESS params.");
        socketAgent->SetSocketAgentState(CAE::WAIT_CLOSE);
        CaeAgent::GetInstance().CloseClient();
        return false;
    }
    CaeCommon::PrintfCurrentTime("Send start param success.");
    return true;
}

void CaeConnectionAgent::VerifyResultCallback(bool success, bool secondVerify,
                                              const std::string& connectionId, const std::string& ClientPlatformType)
{
    LOGI("Client %s verify result: %d, second verify: %d", connectionId.c_str(), success, secondVerify);
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    std::lock_guard<std::mutex> lock(m_mutex);
    {
        if (connection == nullptr) {
            LOGE("Client is invalid, not exist or freed.");
            return;
        }

        connection->SetIsVerifyComplete(true);
        if (!success) {
            connection->DisableSend();
            connection->SetSocketAgentState(CAE::WAIT_CLOSE);
            CaeAgent::GetInstance().CloseClient();
            return;
        }

        connection->SetSocketAgentState(CAE::VERIFYED);

        // 抢占 or 独占
        if (!ProcessConnectionMode(connectionId, connection)) {
            return;
        }
    }

    if (m_connectionPool->Size() == 0) {
        return;
    }

    if (!secondVerify) {
        if (!ProcessResource(connection)) {
            return;
        }
    }

    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_VERIFY_SUCCESS, connection);
    CaeCommon::PrintfCurrentTime("End verify the command");
    CaeAgent::GetInstance().SetStatus(CaeAgentStatus::CAE_AGENT_RUNING);

    if (!ProcessTimer(connection)) {
        return;
    }

    if (!ProcessMedia(connection)) {
        return;
    }

    if (!ProcessNotifyToClient(connection)) {
        return;
    }

    connection->SetSocketAgentState(CAE::RUNNING);

    if (connection->GetConnectionParams()->IsScreenshot()) {
        return;
    }

    if (CaeConfigManage::EnableRemoteIme()) {
        CaeImeControl::GetInstance().EnableRemoteIme(ClientPlatformType);
    }

    SendVirtualDeviceStartRequest();
}

void CaeConnectionAgent::ActionCallback(const std::string& connectionId, COMMAND_TYPE commandType)
{
    switch (commandType) {
        case COMMAND_TYPE::CMD_STOP:
            StopAction(connectionId);
            break;
        case COMMAND_TYPE::CMD_PAUSE:
            PauseAction(connectionId);
            break;
        case COMMAND_TYPE::CMD_RESUME:
            ResumeAction(connectionId);
            break;
        case COMMAND_TYPE::CMD_RECONNECT:
            ReconnectAction(connectionId);
            break;
        default:
            break;
    }
}

void CaeConnectionAgent::StopAction(const std::string& connectionId)
{
    LOGI("Client %s stoped.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    connection->SetSocketAgentState(CAE::WAIT_CLOSE);
    CaeAgent::GetInstance().CloseClient();
}

void CaeConnectionAgent::PauseAction(const std::string& connectionId)
{
    LOGI("Client %s paused.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    connection->SetSocketAgentState(CAE::PAUSED);
    connection->StartPauseTimer();
    connection->SetPauseTimeoutCallback([this](const std::string& connectionId)
                                        {CaeConnectionAgent::PauseTimeoutCallback(connectionId);});

    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    uint32_t otherValidClientCnt = 0;
    uint32_t otherValidScreenshotClientCnt = 0;
    for (const auto& conn : connections) {
        if (conn->GetConnectionParams()->IsScreenshot()) {
            otherValidScreenshotClientCnt++;
        } else {
            otherValidClientCnt++;
        }
    }

    if (otherValidClientCnt == 0) {
        CaeEngineControl::GetInstance().CloseMediaStream();
        CaeEngineControl::GetInstance().CloseVirtualDevices();
        CaeAgent::GetInstance().ClearPipesData();
    } else {
        LOGI("Have %d clients already running.", otherValidClientCnt);
    }

    if (otherValidScreenshotClientCnt == 0) {
        CaeEngineControl::GetInstance().CloseScreenshot();
        CaeAgent::GetInstance().ClearScreenshotPipesData();
    } else {
        LOGI("Have %d clients already screenshot.", otherValidScreenshotClientCnt);
    }
}

void CaeConnectionAgent::ResumeAction(const std::string& connectionId)
{
    LOGI("Client %s resumed.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    connection->StopPauseTimer();

    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAllValid();
    uint32_t otherValidClientCnt = 0;
    uint32_t otherValidScreenshotClientCnt = 0;
    for (const auto& conn : connections) {
        if (conn->GetConnectionParams()->IsScreenshot()) {
            otherValidScreenshotClientCnt++;
        } else {
            otherValidClientCnt++;
        }
    }

    connection->SetSocketAgentState(CAE::RUNNING);

    if (connection->GetConnectionParams()->IsScreenshot()) {
        if (otherValidScreenshotClientCnt > 0) {
            LOGI("Have %d clients already running.", otherValidScreenshotClientCnt);
        } else {
            uint32_t jpegWidth = connection->GetConnectionParams()->GetJpegWidth();
            uint32_t jpegHeight = connection->GetConnectionParams()->GetJpegHeight();
            Result ret = CaeEngineControl::GetInstance().OpenScreenShots(jpegWidth, jpegHeight);
        }
        CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SWITCH_FOREGROUND_SUCCESS, connection);
        return;
    }

    if (otherValidClientCnt > 0) {
        LOGI("Have %d clients already running.", otherValidClientCnt);
        return;
    }

    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SWITCH_FOREGROUND_SUCCESS, connection);
    CaeEngineControl::GetInstance().OpenMediaStream();
    CaeEngineControl::GetInstance().OpenVirtualDevices(connection->GetSdkVersion());
    SendVirtualDeviceStartRequest();
}

void CaeConnectionAgent::ReconnectAction(const std::string& connectionId)
{
    LOGI("Client %s reconnecting.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection== nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    std::string newSessionId = connection->GetConnectionParams()->GetSessionId();

    bool reconnectValid = false;
    bool isScreenshot = false;
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
    for (const auto& conn : connections) {
        CAE::CaeSocketAgentState state = conn->GetSocketAgentState();
        if (conn->GetSocketConnectionId() == connectionId) {
            continue;
        }
        if (conn->GetConnectionParams()->GetSessionId() == newSessionId) {
            if (conn->GetSocketAgentState() != CAE::WAIT_RECONNECT
                && conn->GetSocketAgentState() != CAE::PAUSED_DISCONNECT) {
                LOGI("This reconnect need restart mtrans.");
                StopMtrans();
                StartMtrans();
            }
            // 恢复重连之前保存的参数
            shared_ptr<CaeParamStorage> paramStorage = conn->GetConnectionParams();
            connection->SetConnectionParams(paramStorage);
            conn->SetSocketAgentState(CAE::WAIT_CLOSE);
            reconnectValid = true;
            isScreenshot = conn->GetConnectionParams()->IsScreenshot();
            break;
        }
    }

    if (reconnectValid) {
        if (isScreenshot) {
            int num = QueryScreenshotClientNum();
            connection->SetIsVerifyComplete(true);
            connection->SetSocketAgentState(CAE::RUNNING);
            connection->EnableSend();
            if (num == 0) {
                uint32_t jpegWidth = connection->GetConnectionParams()->GetJpegWidth();
                uint32_t jpegHeight = connection->GetConnectionParams()->GetJpegHeight();
                Result ret = CaeEngineControl::GetInstance().OpenScreenShots(jpegWidth, jpegHeight);
            }
            CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RECONNECT_SUCCESS, connection);
        } else {
            bool trailTimeOut = CaeClientNotifyTool::GetInstance().GetTrailTimeoutState();
            bool touchTimeOut = CaeClientNotifyTool::GetInstance().GetTouchTimeoutState();
            if (trailTimeOut || touchTimeOut) {
                if (trailTimeOut) {
                    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_TRAIL_PLAY_TIMEOUT, connection);
                }
                if (!trailTimeOut && touchTimeOut) {
                    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_NOTOUCH_TIMEOUT, connection);
                }
            }
            connection->SetIsVerifyComplete(true);
            connection->SetSocketAgentState(CAE::RUNNING);
            connection->EnableSend();
            CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RECONNECT_SUCCESS, connection);
            CaeEngineControl::GetInstance().OpenMediaStream();
            CaeEngineControl::GetInstance().OpenVirtualDevices(connection->GetSdkVersion());
            SendVirtualDeviceStartRequest();
        }
        LOGI("Client %s reconnected.", connectionId.c_str());
    } else {
        LOGI("Client %s reconnect invalid.", connectionId.c_str());
        connection->EnableSend();
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_CONNECT_LOST);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, connection)) {
            LOGE("Failed to send reconnect sessionId invalid msg to client.");
        }
        connection->SetSocketAgentState(CAE::WAIT_CLOSE);
    }

    CaeAgent::GetInstance().CloseClient();
}

void CaeConnectionAgent::DisconnectCallback(const std::string& connectionId)
{
    LOGI("Client %s disconnect callback.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    connection->DisableSend();
    CAE::CaeSocketAgentState state = connection->GetSocketAgentState();
    if (state < CAE::VERIFYED) {
        LOGI("Client not verified, mark as wait close.");
        connection->SetSocketAgentState(CAE::WAIT_CLOSE);
        CaeAgent::GetInstance().CloseClient();
        return;
    } else if (state >= CAE::WAIT_CLOSE) {
        LOGI("Client has been marked wait close or deinit, do nothing.");
        return;
    } else {
        if (state == CAE::PAUSED) {
            connection->SetSocketAgentState(CAE::PAUSED_DISCONNECT);
        } else {
            connection->SetSocketAgentState(CAE::WAIT_RECONNECT);
        }

        if (connection->GetConnectionParams()->IsScreenshot()) {
            int num = QueryScreenshotClientNum();
            if (num == 0) {
                CaeEngineControl::GetInstance().CloseScreenshot();
            }
            return;
        }

        CaeEngineControl::GetInstance().CloseMediaStream();
        CaeEngineControl::GetInstance().CloseVirtualDevices();

        StopMtrans();
        StartMtrans();
    }
}

void CaeConnectionAgent::DisconnectTimeoutCallback(const std::string& connectionId)
{
    LOGI("Client %s disconnect timeout callback.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    connection->SetSocketAgentState(CAE::WAIT_CLOSE);
    CaeAgent::GetInstance().CloseClient();
}

void CaeConnectionAgent::NotouchTimeoutCallback(const std::string& connectionId)
{
    LOGI("Client %s no touch timeout callback.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_NOTOUCH_TIMEOUT, connection);
}

void CaeConnectionAgent::TrialTimeoutCallback(const std::string& connectionId)
{
    LOGI("Client %s trail timeout callback.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_TRAIL_PLAY_TIMEOUT, connection);
}

void CaeConnectionAgent::PauseTimeoutCallback(const std::string& connectionId)
{
    LOGI("Client %s pause timeout callback.", connectionId.c_str());
    std::shared_ptr<CaeSocketAgent> connection = m_connectionPool->GetIfExist(connectionId);
    if (connection == nullptr) {
        LOGE("Client is invalid, not exist or freed.");
        return;
    }

    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_HOME_TIMEOUT, connection);
}

void OnRemoteAddressChanged(string ip, uint16_t port)
{
    LOGI("Mtrans remote address changed, %s:%d", ip.c_str(), port);
    CaeEngineControl::GetInstance().RequestIframe();
}

void CaeConnectionAgent::SendScreenshotDataHook(void *pkt, int size)
{
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
    for (const auto& connection : connections) {
        if (connection->IsVerifyComplete() && connection->GetSocketAgentState() == CAE::RUNNING
            && connection->GetConnectionParams()->IsScreenshot()) {
            int ret = connection->Send(pkt, size);
            if (ret < 0) {
                LOGE("send one frame error, client is [%s]", connection->GetSocketConnectionId().c_str());
            }
        }
    }
}

int CaeConnectionAgent::QueryScreenshotClientNum()
{
    int num = 0;
    std::vector<std::shared_ptr<CaeSocketAgent>> connections = m_connectionPool->GetAll();
    for (const auto& connection : connections) {
        if (connection->IsVerifyComplete() && connection->GetSocketAgentState() == CAE::RUNNING
            && connection->GetConnectionParams()->IsScreenshot()) {
            num++;
        }
    }
    return num;
}

void CaeConnectionAgent::InitScreenshotDataHandlers()
{
    CaeDataPipe* pipe = CaeAgent::GetInstance().GetScreenshotDataPipe();
    if (pipe == nullptr) {
        LOGE("Failed to create screenshot pipe data.");
    }
    m_screenshotPipeDataHandleThread = make_unique<CaeScreenshotPipeDataHandleThread>(pipe);
    if (m_screenshotPipeDataHandleThread == nullptr) {
        LOGE("Failed to create screenshot pipe data handle thread.");
    } else {
        m_screenshotPipeDataHandleThread->SetScreenshotDataSendHook([this](void *pkt, size_t size) ->
                                                                            void{CaeConnectionAgent::SendScreenshotDataHook(pkt, size);});
        m_screenshotPipeDataHandleThread->Start();
        LOGI("Start screenshot pipe data handle thread. ");
    }
}

void CaeConnectionAgent::DeInitScreenshotPipeDataHandlers()
{
    if (m_screenshotPipeDataHandleThread != nullptr) {
        m_screenshotPipeDataHandleThread->Stop();
        while (m_screenshotPipeDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(VIDEO_HANDLER_THREAD_SLEEP_INTERVAL);
        }
        m_screenshotPipeDataHandleThread = nullptr;
    }
}

int32_t OnRecvVideoStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }
    auto *videoData = new uint8_t[length];
    (void) memcpy_s(videoData, length, data, length);

    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, videoData, length);
    delete[]videoData;
    videoData = nullptr;
    return 0;
}

bool OnIsVerifiedIpCallBack(std::string ip)
{
    return CaeAgent::GetInstance().IsVerifiedIp(ip);
}

int32_t OnRecvAudioStreamData(uint8_t *data, uint32_t length)
{
    uint32_t msgHeaderLen = 36;
    if (length < msgHeaderLen) {
        return -1;
    }
    auto *audioData = new uint8_t[length - msgHeaderLen];
    (void) memcpy_s(audioData, length - msgHeaderLen, data + msgHeaderLen, length - msgHeaderLen);

    CaeEngineControl::GetInstance().VmiDeviceSend(MICROPHONE, audioData, length - msgHeaderLen);
    delete[]audioData;
    audioData = nullptr;
    return 0;
}

int32_t OnRecvSensorDataStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }

    auto *sensorData = new uint8_t[length];
    (void) memcpy_s(sensorData, length, data, length);
    CaeEngineControl::GetInstance().VmiDeviceSend(SENSOR, sensorData, length);
    delete[] sensorData;
    sensorData = nullptr;
    return 0;
}

int32_t OnRecvLocationStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }

    auto *locationData = new uint8_t[length];
    (void) memcpy_s(locationData, length, data, length);
    CaeEngineControl::GetInstance().HandleLocationMsg(locationData, length);
    delete[] locationData;
    locationData = nullptr;
    return 0;
}

int32_t OnRecvTouchEventStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }

    auto *touchData = new uint8_t[length];
    (void) memcpy_s(touchData, length, data, length);
    auto *touchMsg = (CaeTouchEventMsg *) (touchData);
    CaeEngineControl::GetInstance().HandleTouchMsg(touchMsg);

    delete[] touchData;
    touchData = nullptr;
    return 0;
}

int32_t OnRecvKeyEventStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }

    auto *keyData = new uint8_t[length];
    (void) memcpy_s(keyData, length, data, length);
    auto *keyMsg = (CaeKeyEventMsg *) (keyData);
    CaeEngineControl::GetInstance().HandleKeyMsg(keyMsg);
    if (keyMsg->GetAction() == 1 && keyMsg->GetKeycode() == 4 && IsBackHome()) {
        LOGI("Back to home when window is desk or lockscreen");
        SendMsgToClient(CAE_BACK_HOME);
    }
    delete keyMsg;
    keyMsg = nullptr;
    return 0;
}

bool IsBackHome()
{
    char dumpsysRes[1024];
    memset_s(dumpsysRes, sizeof(dumpsysRes), '\0', sizeof(dumpsysRes));

    string dumpsysCmd = "dumpsys window | grep -E 'mCurrentFocus|mIsShowing'";
    string lockScreen = "mIsShowing=true";
    string deskLauncher = CaeConfigManage::GetDeskLauncher();
    bool isBackToHome = false;

    FILE *fp = popen(dumpsysCmd.c_str(), "r");
    while (fgets(dumpsysRes, sizeof(dumpsysRes) - 1, fp) != nullptr) {
        string dumpsysResultStr = string(dumpsysRes);
        if (dumpsysResultStr.find(deskLauncher) != string::npos || dumpsysResultStr.find(lockScreen) != string::npos) {
            isBackToHome = true;
            break;
        }
    }
    pclose(fp);
    return isBackToHome;
}

int32_t OnRecvMotionEventStreamData(uint8_t *data, uint32_t length)
{
    if (length <= 0) {
        return -1;
    }

    auto *motionData = new uint8_t[length];
    (void) memcpy_s(motionData, length, data, length);
    CaeEngineControl::GetInstance().HandleMotionMsg(motionData, length);
    delete[] motionData;
    motionData = nullptr;
    return 0;
}

int32_t OnRecvCmdData(uint8_t *data, uint32_t length)
{
    return 0;
}

inline void CalculateNewResolution(uint32_t &width, uint32_t &height)
{
    uint32_t physicalWidth = CaeMediaConfig::GetInstance().GetPhysicalWidth();
    uint32_t physicalHeight = CaeMediaConfig::GetInstance().GetPhysicalHeight();

    if (((float)physicalWidth / (float)physicalHeight) >= ((float)width / (float)height)) {
        height = static_cast<uint32_t>(physicalHeight * width / physicalWidth);
        height = height % 8 == 0 ? height : (height / 8 + 1) * 8;
    } else {
        width = static_cast<uint32_t>(physicalWidth * height / physicalHeight);
        width = width % 8 == 0 ? width : (width / 8 + 1) * 8;
    }
}

void OnEncBitrateCallback(uint32_t encBitrate, uint32_t totalBitrate)
{
    if (!CaeMediaConfig::GetInstance().IsAutoQuality()) {
        return;
    }

    LOGI("Encode bitrate callback, encBitrate: %d, totalBitrate: %d", encBitrate, totalBitrate);

    uint32_t targetBitrate = encBitrate * 1000;

    uint32_t adjustBitrate = 0;
    uint32_t frameRate = 0;
    uint32_t width = 0;
    uint32_t height = 0;
    if (CaeConfigManage::GetHardwareWidth() < 1080) {
        if (targetBitrate < 100000) {
            adjustBitrate = 100000;
        } else if (targetBitrate >= 3000000) {
            adjustBitrate = 3000000;
        } else {
            adjustBitrate = targetBitrate * 0.8;
        }
    } else {
        if (targetBitrate >= 0 && targetBitrate < 750000) {
            adjustBitrate = targetBitrate < 100000 ? 100000 : targetBitrate;
            frameRate = 30;
            width = 720;
            height = 1280;
            CalculateNewResolution(width, height);
        } else if (targetBitrate >= 750000 && targetBitrate < 1500000) {
            adjustBitrate = targetBitrate;
            frameRate = 30;
            width = 720;
            height = 1280;
            CalculateNewResolution(width, height);
        } else if (targetBitrate >= 1500000 && targetBitrate < 4000000) {
            adjustBitrate = targetBitrate;
            frameRate = 30;
            width = 1080;
            height = 1920;
            CalculateNewResolution(width, height);
        } else {
            adjustBitrate = targetBitrate >= 5000000 ? 5000000 : targetBitrate;
            frameRate = 60;
            width = 1080;
            height = 1920;
            CalculateNewResolution(width, height);
        }
    }

    char streamWidth[5] = {0};
    char streamHeight[5] = {0};

    char fps[3] = {0};
    char bitrate[20] = {0};
    errno_t rt;
    rt = sprintf_s(streamWidth, sizeof(streamWidth), "%u", width);
    if (rt == -1) {
        LOGE("sprintf streamWidth failed.");
        return;
    }
    rt = sprintf_s(streamHeight, sizeof(streamHeight), "%u", height);
    if (rt == -1) {
        LOGE("sprintf streamHeight failed.");
        return;
    }
    rt = sprintf_s(fps, sizeof(fps), "%u", frameRate);
    if (rt == -1) {
        LOGE("sprintf fps failed.");
        return;
    }
    rt = sprintf_s(bitrate, sizeof(bitrate), "%u", adjustBitrate);
    if (rt == -1) {
        LOGE("sprintf bitrate failed.");
        return;
    }

    if (CaeConfigManage::GetHardwareWidth() >= 1080) {
        CaeMediaConfig::GetInstance().SetStreamWidth(streamWidth);
        CaeMediaConfig::GetInstance().SetStreamHeight(streamHeight);
        CaeMediaConfig::GetInstance().SetFrameRate(fps);
    }
    CaeMediaConfig::GetInstance().SetBitrate(bitrate, false);
    LOGI("Update encode param, resolution: [%d x %d], bitrate: %d, fps: %d", width, height,
         adjustBitrate, frameRate);
    Result ret = CaeEngineControl::GetInstance().SetMediaConfig();
    if (ret != SUCCESS) {
        LOGE("Update encode param failed.");
    }
}

void OnEncKeyFrameCallback()
{
    Result ret = CaeEngineControl::GetInstance().RequestIframe();
    if (ret != SUCCESS) {
        LOGE("Request key frame failed.");
    }
}

void SendMsgToClient(const int msg)
{
    map<string, string> params = CaeAppCtrlCmdUtils::GenerateNotifyParams(msg);
    string cmdStr = CaeAppCtrlCmdUtils::GenerateCommand(params);
    size_t strLen = cmdStr.size();

    std::shared_ptr<uint8_t> sendBuff;
    sendBuff = std::shared_ptr<uint8_t>(new uint8_t[strLen + sizeof(STREAM_MSG_HEAD)], std::default_delete<uint8_t[]>());
    if (sendBuff == nullptr) {
        LOGE("Failed to malloc sendBuff.");
        return;
    }

    StreamMsgHead msgHead {};
    msgHead.type = CAE_MSG_CONTROL;
    msgHead.SetPayloadSize(static_cast<uint32_t>(strLen));
    msgHead.magicWord = CAE_STREAM_DELIMITER_MAGICWORD;
    msgHead.checksum = static_cast<uint8_t>(GET_CAE_CHECKSUM(CAE_MSG_CONTROL));
    if (EOK != memcpy_s(sendBuff.get(), sizeof(STREAM_MSG_HEAD), &msgHead, sizeof(STREAM_MSG_HEAD))) {
        LOGE("Failed to memcpy msgHead to sendBuff.");
        sendBuff = nullptr;
        return;
    }

    if (EOK != memcpy_s(sendBuff.get() + sizeof(STREAM_MSG_HEAD), strLen, cmdStr.c_str(), strLen)) {
        LOGE("Failed to memcpy cmdStr to sendBuff.");
        sendBuff = nullptr;
        return;
    }

    int bytes = CaeAgent::GetInstance().SendCustomDataToClient(sendBuff, static_cast<int>(strLen + sizeof(STREAM_MSG_HEAD)), msgHead.type);
    sendBuff = nullptr;
    if (bytes <= 0) {
        LOGE("Failed to Send msg %s to client.", cmdStr.c_str());
    } else {
        LOGD("Send msg to client: type: %c, Send %s to client: %d.", msgHead.type, cmdStr.c_str(), bytes);
    }
}